require 'date'
require 'multi_field_date'
require 'acts_as_multi_field_date'
module ActiveRecord
  module Acts
    module MFD 
      module SingletonMethods

        #returns array of years for select menu.  
        #options is a hash of :start_year and/or :end_year
        def years_for_select(ops={})
          defaults = {:start_year => 1800, :end_year => Date.today.year}
          ops = defaults.merge(ops)
          yrs = (ops[:start_year]..ops[:end_year]).to_a.map{|a| [a, a]}
          yrs = yrs.reverse if ops[:reverse]
          yrs.unshift(['Year','']) if ops[:show_default]
          yrs
        end

        #returns array of month numbers. 
        def months_for_select(ops={})
          defaults = {:start_month => 1, :end_month => 12}
          ops = defaults.merge(ops)
          months = [['January',1],['February',2],['March',3],['April',4],
          ['May',5],['June',6],['July',7],['August',8],['September',9],
          ['October',10],['November',11],['December',12]]
          mnths = months[ops[:start_month]-1..ops[:end_month]-1]
          mnths.unshift(['Month','']) if ops[:show_default]
          mnths
        end

        def month_abbrs_for_select(ops={})
          defaults = {:start_month => 1, :end_month => 12}
          ops = defaults.merge(ops)
          months = [['Jan',1],['Feb',2],['Mar',3],['Apr',4],
          ['May',5],['Jun',6],['Jul',7],['Aug',8],['Sep',9],
          ['Oct',10],['Nov',11],['Dec',12]]
          mnths = months[ops[:start_month]-1..ops[:end_month]-1]
          mnths.unshift(['Month','']) if ops[:show_default]
          mnths
        end
        def days_for_select(ops={})
          defaults = {:month => nil}
          ops = defaults.merge(ops)
          if ops[:month].nil?
            dys = (1..31).to_a.map{|d| [d, d]}
          end
          dys.unshift(['Day','']) if ops[:show_default]
          dys 
        end
      end #SingletonMethods

      module DateValidator
        def self.valid_date?(year, month, day)
          return true if month.nil? or day.nil?
          return false if day == 0 or month == 0 or month > 12
          days_in_month = DateValidator.days_in_month(month, year)
          return false if day > days_in_month
          return true
        end

        #from ActiveSupport::CoreExtensions::Time
        def self.days_in_month(month, year=nil)
          if month == 2
            !year.nil? && (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)) ?  29 : 28
          elsif month <= 7
            month % 2 == 0 ? 30 : 31
          else
            month % 2 == 0 ? 31 : 30
          end
        end
      end #DateValidator

      module Helpers
        #helper method to zero-pad an integer (0-9)
        def zero_pad(num)
          raise ArgumentError("number must be an Integer") unless num.is_a?(Integer)
          return "0#{num}" if num < 10
          return "#{num}"
        end
      end #Helpers

      #Can't think of a better name.. used to just be MultiFieldDate, but that was confusing
      #since that's the name of the parent module
      class MultiFieldDateClass
        include ActiveRecord::Acts::MFD::Helpers
        cattr_accessor :year, :month, :day, :date
        attr_accessor  :year, :month, :day, :date

        def initialize(date, year, month, day)
          @date, @year, @month, @day = date, year, month, day
        end

        def year
          return @year if @year
          return @date.year if @date
        end  

        def month 
          return @month if @month
          return @date.month if @date
        end  

        def day
          return @day if @day
          return @date.day if @date
        end  

        #Updates the date field in the parent model if @year, @month, and @day are 
        #all present
        def update_date 
          if @year and @month and @day
            begin 
              @date =  Date.new(@year.to_i, @month.to_i, @day.to_i)
            rescue
              @date = nil
            end
          else
            @date = nil
          end
        end

        #Returns date text with missing elements (year, month, day) 
        #replaced by <tt>missing_char</tt> 
        #E.g., if only the year is present, the returned value would be something like:
        #2007-??-??
        def date_text(missing_char='?')
          x = missing_char
          return @date.to_s if @date
          txt = ''
          txt << (@year.nil? ? "#{x}#{x}#{x}#{x}" : @year.to_s) << "-"
          txt << (@month ? zero_pad(@month) : "#{x}#{x}" ) << "-"
          txt << (@day ? zero_pad(@day) : "#{x}#{x}" ) 
          txt
        end

      end #MultiFieldDateClass

    end #MFD
  end #Acts
end #ActiveRecord

